﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Drawing;
using System.Collections;

using Noris.Schedule.Support;
using Noris.Schedule.Support.Data;
using Noris.Schedule.Support.Sql;
using Noris.Schedule.Support.Core;
using Noris.Schedule.Support.Services;

using Noris.Schedule.Planning.DataFace;

// Tento soubor obsahuje jednotky pro komunikaci plánovacího procesu s vizuální vrstvou
namespace Noris.Schedule.Planning.ProcessData
{
	#region CLASS DataVisualCls : DATOVÝ KONEKTOR PRO VIZUÁLNÍ TABULI DO PLÁNOVACÍHO PROCESU
	/// <summary>
	/// Konektor do plánovacího procesu ze strany vizuální prezentace.
	/// Poskytuje přístup k potřebným datům plánovacího procesu a vstupní metody pro řízení změn dat.
	/// </summary>
	public class DataVisualCls : PlanningComponentCls, IDataSource, IDisposable
	{
		#region KONSTRUKCE
		internal DataVisualCls(PlanningDataCls parent)
		{
			this._Parent = parent;
			this._WorkQueuePrepare();
		}
		public void Dispose()
		{ }
		#endregion
		#region PŘÍSTUP DO POTŘEBNÝCH BLOKŮ PLÁNOVACÍHO PROCESU
		/// <summary>
		/// Parent = plánovací základna
		/// </summary>
		internal PlanningDataCls Parent { get { return this._Parent; } }
		private PlanningDataCls _Parent;
		/// <summary> { get; }
		/// Data jednotlivých plánovacích os (osa materiálu, přehled kapacitních úkolů, systém vztahů)
		/// </summary>
		internal AxisHeapCls AxisHeap { get { return this.Parent.AxisHeap; } }
		/// <summary> { get; }
		/// Data o probíhající výrobě (příkazy, operace, komponenty, zdroje)
		/// </summary>
		internal ProductDataCls ProductData { get { return this.Parent.ProductData; } }
		/// <summary> { get; }
		/// Data o materiálu (zásoby, materiálové osy)
		/// </summary>
		internal MaterialDataCls MaterialData { get { return this.Parent.MaterialData; } }
		/// <summary> { get; }
		/// Data o kapacitách (zdroje, kapacitní jednotky, kalendáře), registrační systém
		/// </summary>
		internal CapacityRegisterCls CapacityRegisterData { get { return this.Parent.CapacityRegisterData; } }
		/// <summary> { get; }
		/// Systém pro správu kapacitních jednotek, zdrojů kapacit a vztahy mezi nimi
		/// </summary>
		internal CapacityDataCls CapacityData { get { return this.Parent.CapacityRegisterData.CapacityData; } }
		#endregion
		#region PUBLIC METODY : ZAŘAZENÍ POŽADAVKU DO FRONTY PRÁCE
		/// <summary>
		/// Vlastnosti datového zdroje. Jde o statické informace.
		/// </summary>
		DataSourceProperties IDataSource.Properties
		{
			get
			{
				DataSourceProperties properties = new DataSourceProperties();
				properties.IsAsynchronous = true;
				properties.MakeRelationMap = true;
				return properties;
			}
		}
		/// <summary>
		/// Aktuální informace, zda datový zdroj může přijmout asynchronní požadavek.
		/// Má vrátit true tehdy, pokud je na to naprogramován,
		/// a současně v delegátu this.RequestCompleted je odkaz na metodu, kterou je možno volat po dokončení requestu.
		/// </summary>
		bool IDataSource.AcceptRequestAsync { get { return (this._RequestCompleted != null); } }
		/// <summary>
		/// Zařadí požadavek do fronty práce, hned na její začátek, a počká na dokončení.
		/// Výsledky procesu jsou uloženy v parametru request.
		/// Po dokončení není volán delegát RequestCompleted.
		/// </summary>
		/// <param name="request">Požadovaná operace</param>
		void IDataSource.RunRequestSync(DataSourceRequest request)
		{
			this._WorkQueue.AddRequestSync(request);
		}
		/// <summary>
		/// Zařadí požadavek do fronty práce, nečeká na její dokončení, ihned vrací řízení.
		/// Po dokončení jsou výsledky odeslány do delegáta RequestCompleted.
		/// Výsledky procesu jsou do něj předány v parametru request.
		/// Pokud není vyplněn delegát RequestCompleted, je vyvolána chyba.
		/// </summary>
		/// <param name="request">Požadovaná operace</param>
		void IDataSource.RunRequestAsync(DataSourceRequest request)
		{
			if (this._RequestCompleted == null)
				Throw.SysError(MessageInfo.Get("Byl proveden pokus o použití asynchronní metody DataSource.RunRequestAsync v datovém zdroji, kde není definován delegát RequestCompleted."));
			this._WorkQueue.AddRequestAsync(request);
		}
		/// <summary>
		/// Metoda upřednostní požadavky aktivního grafu na úkor požadavků neaktivních grafů.
		/// Jinými slovy, zajistí, že asynchronní požadavky, která vydal aktivní graf, se budou realizovat dříve, než požadavky jiných grafů.
		/// Metoda se typicky volá po přepnutí aktivního grafu. Zajistí, že požadavky nyní aktivního grafu se vyřídí dříve, 
		/// než dojde řada na vyřizování nevyřízených požadavků z grafů, které nejsou aktivní.
		/// </summary>
		/// <param name="activeGraphId"></param>
		void IDataSource.ActivateAsyncRequestForGraphId(int activeGraphId)
		{
			this._WorkQueue.ScanAllRequest(delegate(DataSourceRequest request) { request.SetActiveGraph(activeGraphId); });
		}
		/// <summary>
		/// Řízení asynchronní operace datového zdroje, umožní pozastavit / rozběhnout asynchronní operace.
		/// false = default = asynchronní operace normálně probíhají / true = asynchronní operace jsou pozastaveny.
		/// Pokud datový zdroj nepodporuje asynchronní operace (deklaruje CanBeAsynchronous = false), nechť implementuje standardní property bez rozšířené funkcionality.
		/// Pokud datový zdroj podporuje asynchronní operace, může pro jejich řízení využít třídu Aps.Core.WorkQueueCls, kde je property WaitAsyncQueue řádně implementována.
		/// Pak stačí propojit property IDataSource.WaitAsyncQueue na WorkQueueCls.WaitAsyncQueue
		/// </summary>
		bool IDataSource.WaitAsyncQueue { get { return this._WorkQueue.WaitAsyncQueue; } set { this._WorkQueue.WaitAsyncQueue = value; } }
		/// <summary>
		/// Vrací počet čekajících asynchronních operací, typicky pro zobrazení ve stavovém řádku.
		/// Pokud datový zdroj nepodporuje asynchronní operace (deklaruje CanBeAsynchronous = false), nechť vrátí 0.
		/// Pokud datový zdroj podporuje asynchronní operace, může pro jejich řízení využít třídu Aps.Core.WorkQueueCls, kde je property AsynchronousOperationCount řádně implementována.
		/// Pak stačí propojit property IDataSource.AsynchronousOperationCount na WorkQueueCls.AsynchronousOperationCount
		/// </summary>
		int IDataSource.AsynchronousOperationCount { get { return this._WorkQueue.AsynchronousOperationCount; } }
		/// <summary>
		/// Delegát na metodu, která bude spuštěna vždy poté, kdy je dokončena asynchronní práce na jednom každém požadavku.
		/// Metoda dostává jako parametr ten požadavek, který byl předán k vyřízení.
		/// Požadavek má vyplněné Result údaje.
		/// Tento delegát se nevolá pro synchronní požadavky.
		/// </summary>
		Action<DataSourceRequest> IDataSource.RequestCompleted { get { return _RequestCompleted; } set { _RequestCompleted = value; } }
		private Action<DataSourceRequest> _RequestCompleted;
		#endregion
		#region FRONTA PRÁCE - IMPLEMENTACE, VÝKONNÉ A NOTIFIKAČNÍ METODY, ZPRACOVÁNÍ JEDNOTLIVÉHO POŽADAVKU (AŽ NA NĚJ PŘIJDE ČAS)
		/// <summary>
		/// Připraví si funkční frontu pro interní řízení zpracování požadavků.
		/// </summary>
		private void _WorkQueuePrepare()
		{
			this._WorkQueue = new WorkQueueCls<DataSourceRequest>(this._DoWork, this._NotifyDone);
			this._WorkQueue.WorkQueueCountChanged += new WorkQueueCountChangedDelegate(this._WorkQueueCountChanged);
		}
		/// <summary>
		/// Místní implementace fronty práce
		/// </summary>
		private WorkQueueCls<DataSourceRequest> _WorkQueue;
		/// <summary>
		/// Pracovitá metoda: je vyvolávaná z fronty práce tehdy,
		/// když v ní bude dokončeno zpracování jednoho požadavku a přijde čas na zpracování dalšího požadavku.
		/// </summary>
		/// <param name="request"></param>
		private void _DoWork(DataSourceRequest request)
		{
			try
			{
				switch (request.RequestType)
				{
					case DataSourceRequestType.TopLevelRead:
					case DataSourceRequestType.SubRowsRead:
						_ReadRows(request);
						break;
					case DataSourceRequestType.ElementsRead:
						_ReadElements(request);
						break;
					case DataSourceRequestType.ElementMoveToTime:
						_SetChange(request);
						break;
					case DataSourceRequestType.CreateDataRelationNet:
						_CreateRelations(request);
						break;
					case DataSourceRequestType.ElementsMapRead:
						_ReadElementsMap(request);
						break;
				}
			}
			catch (Exception exc)
			{
				request.ResultException = exc;
			}
		}
		/// <summary>
		/// Ohlášení dokončení jedné práce, odsud do nadřízeného delegáta.
		/// Metodu volá fronta práce poté, kdy se jí vrátí řízení z jednoho požadavku.
		/// Metoda zajistí vyvolání eventu do nadřízeného objektu.
		/// </summary>
		/// <param name="request"></param>
		private void _NotifyDone(DataSourceRequest request)
		{
			if (this._RequestCompleted != null)
				this._RequestCompleted(request);
		}
		#endregion
		#region EVENTY
		/// <summary>
		/// Event, který se volá po každé změně počtu asynchronních požadavků
		/// </summary>
		public event WorkQueueCountChangedDelegate AsyncRequestCountChanged;
		/// <summary>
		/// Invoker eventu WorkQueueCountChanged
		/// </summary>
		/// <param name="count"></param>
		/// <param name="increased"></param>
		protected virtual void OnAsyncRequestCountChanged(object sender, WorkQueueCountChangedEventArgs e)
		{
			if (AsyncRequestCountChanged != null)
				AsyncRequestCountChanged(sender, e);
		}
		/// <summary>
		/// Handler události _WorkQueueCountChanged vyvolané z _WorkQueue
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void _WorkQueueCountChanged(object sender, WorkQueueCountChangedEventArgs e)
		{
			this.OnAsyncRequestCountChanged(sender, e);         // Jen předám dál
		}
		#endregion

		public new void LoadData()
		{
			this.Parent.LoadData();
			this.Parent.LoadWarehouseDocItems();
		}
		public void RunPlanning()
		{
			this.Parent.FillAxisSInitial();
			this.Parent.RunPlanningProcess();
		}

		#region ČTENÍ DAT Z PROCESU DO GUI - ŘÁDKY
		/// <summary>
		/// Vstupní bod požadavku na čtení dat řádků
		/// </summary>
		/// <param name="request"></param>
		private void _ReadRows(DataSourceRequest request)
		{
			if (request.RequestRowGId.AllRecords)
				_ReadRowsAll(request);
			else
				_ReadRowsSub(request);
		}
		/// <summary>
		/// Vstupní bod požadavku na čtení dat všech řádků (základní seznam)
		/// </summary>
		/// <param name="request"></param>
		private void _ReadRowsAll(DataSourceRequest request)
		{
			switch (request.RequestRowGId.ClassNumber)
			{
				case PlanUnitSAxisCls.ClassNr:
					this._ReadRowsAllPlanAxisS(request);
					// this._ReadRowsAllPlanAxisS_Old(request);
					break;
				case PlanUnitCCls.ClassNr:
					this._ReadRowsAllPlanUnitC(request);
					break;
			}
		}
		/// <summary>
		/// Vstupní bod požadavku na čtení dat podřízených řádků (jedna podúroveň)
		/// </summary>
		/// <param name="request"></param>
		private void _ReadRowsSub(DataSourceRequest request)
		{
			switch (request.RequestRowGId.ClassNumber)
			{
				case PlanUnitSAxisCls.ClassNr:
					this._ReadRowsSubPlanAxisS(request);
					break;
			}
		}
		/// <summary>
		/// Vstupní bod požadavku na čtení dat všech řádků plánovací osy S
		/// </summary>
		/// <param name="request"></param>
		private void _ReadRowsAllPlanAxisS_Old(DataSourceRequest request)
		{
			request.ResultItems = new List<IDataRow>();
			IEnumerable<MaterialPlanAxisItemCls> axisTable = this.AxisHeap.AxisS.Values;
			foreach (MaterialPlanAxisItemCls axis in axisTable)
			{
				if (!axis.IsFromWhEnquiry) continue;             // Do tohoto soupisu beru jen výdeje do poptávky.
				PlanningVisualDataRowCls rowMain = this.Parent.VisualGetDataRowForAxisSGanttMain(axis);
				if (rowMain == null) continue;

				// Připravím si seznam podřízených prvků = záznamy osy, a jejich časové úseky, 
				// a z nich určím časový interval nadřízeného (tohoto) hlavního záznamu osy:
				Dictionary<GID, IDataRow> subDict = new Dictionary<GID, IDataRow>();
				VisualTaskTimeCls parentTime = VisualTaskTimeCls.CreateFromEnd(axis.DatePlanEnd);
				this._RowAxisSAddSubTasks_Old(axis.AxisGID, axis.AxisGID, ref parentTime, ref subDict);

				// Do Main řádku osy (mainRow) vložím její sumární čas:
				this._RowAxisSAddElementMain(rowMain, axis, parentTime);
				rowMain.AllElementsLoaded = true;
				rowMain.HasSubNodes = (subDict.Count > 0);

				request.ResultItems.Add(rowMain);

				// Do výsledného soupisu řádků (request.ResultItems) vepíšu ještě všechny podřízené záznamy:
				if (subDict.Count > 0)
					request.ResultItems.AddRange(subDict.Values);
			}
		}
		/// <summary>
		/// Vstupní bod požadavku na čtení dat všech řádků kapacitních jednotek.
		/// </summary>
		/// <param name="request"></param>
		private void _ReadRowsAllPlanUnitC(DataSourceRequest request)
		{
			IEnumerable<CapacityUnitCls> unitList = this.CapacityData.CapacityUnitList;
			foreach (CapacityUnitCls unit in unitList)
			{
				PlanUnitCCls planUnitC = unit.PlanUnitData;
				PlanningVisualDataRowCls item = new PlanningVisualDataRowCls(planUnitC.GId, request.RequestedGraphMode, planUnitC.Reference, planUnitC.Nazev, false);
				request.ResultItems.Add(item);
			}
		}
		#endregion

		#region NAČÍTÁNÍ ŘÁDKŮ OSY S : MAIN ŘÁDKY (POPTÁVKY) A PODŘÍZENÉ ŘÁDKY (VÝROBA DÍLCŮ), VČETNĚ TVORBY ELEMENTŮ (ČASY)
		/// <summary>
		/// Vstupní bod požadavku na čtení dat všech řádků plánovací osy S
		/// </summary>
		/// <param name="request"></param>
		private void _ReadRowsAllPlanAxisS(DataSourceRequest request)
		{
			request.ResultItems = new List<IDataRow>();
			
			// Získám soupis vztahů, které popisují výdej z položek osy S do skladových poptávek:
			GID enquiryGId = new GID(Constants.ClassNumberEnquiry, 0);         // GID třídy Obecná poptávka
			List<PlanningItemRelationCls> enqRelList = this.AxisHeap.FindRelationPrevIDData(enquiryGId);
			foreach (PlanningItemRelationCls enqItem in enqRelList)
			{
				// Pojistka:
				if (enqItem.Relation != PlanningItemRelationType.AxisToEnquiry) continue;

				// Záznam osy S, vytvořit řádek do grafu:
				MaterialPlanAxisItemCls axis = this._FindItemAxisS(enqItem.ItemGIDPrev);
				if (axis == null) continue;
				PlanningVisualDataRowCls rowMain = this.Parent.VisualGetDataRowForAxisSGanttMain(axis);
				if (rowMain == null) continue;

				// Prostor pro tvorbu podřízených řádků, souhrn jejich času:
				List<IDataRow> subList = new List<IDataRow>();
				VisualTaskTimeCls parentTime = VisualTaskTimeCls.CreateFromEnd(axis.DatePlanEnd);

				// Vygenerovat soupis podřízených řádků:
				this._RowAxisSAddSubTasks(axis.AxisGID, ref parentTime, ref subList);

				// Do Main řádku osy (rowMain) vložím její sumární čas:
				this._RowAxisSAddElementMain(rowMain, axis, parentTime);
				rowMain.AllElementsLoaded = true;
				rowMain.HasSubNodes = (subList.Count > 0);

				// Řádek vložím do výsledného soupisu:
				request.ResultItems.Add(rowMain);

				// Do výsledného soupisu řádků (request.ResultItems) vepíšu ještě všechny podřízené záznamy:
				if (subList.Count > 0)
					request.ResultItems.AddRange(subList);
			}
		}
		/// <summary>
		/// Metoda sestaví a vrátí soupis řádků, které představují podřízené pracovní úkoly 
		/// k cílovému záznamu osy S, který je výdejem (do poptávky, do komponenty).
		/// Metoda dostává klíč cílového záznamu osy, datum tohoto výdeje.
		/// Přes referenci plni datový interval (plní počátek pracovního výkonu) 
		/// a přidává položky jednotlivých vizuálních řádků do souhrnu podřízených řádků.
		/// </summary>
		/// <remarks>
		/// Metoda nejprve dohledává skladové převody, které předcházejí danému výdeji, a z nich střádá čas těchto skladových přesunů.
		/// Současně vyhledává tasky, které do těchto skladových přesunů vstupují = výroba výsledného dílce (může jich být více).
		/// Pro každý tento finální task zakládá nový řádek do soupisu řádků.
		/// Pro každý tento finální task dohledává jeho předchozí vztahy, a pokud jde o TaskInner, pak zakládá další elementy do téhož řádku.
		/// Pokud jde o vztah Výdej komponenty (AxisToTask), pak rekurzivně spouští tuto metodu = dohledávám časy převodu, a zakládám řádky pro podřízené dílce.
		/// </remarks>
		/// <param name="parentRowGId">Parent řádek, vždy jeden pro všechny sub levels (sestavuji jednu úroveň podřízených záznam, všechny vážu k jednomu parentu = výdej do poptávky).</param>
		/// <param name="axisGId">Cílový záznam osy S, který je výdejem (výsledek do poptávky, anebo komponenta do nadřízené výroby). Tento GID slouží jako klíč pro hledání podřízených.</param>
		/// <param name="timeRange"></param>
		/// <param name="subRowList"></param>
		private void _RowAxisSAddSubTasks(GID axisGId, ref VisualTaskTimeCls parentTime, ref List<IDataRow> subRowList)
		{
			// Na vstupu není záznam materiálové osy: odmítneme jej:
			if (axisGId.ClassNumber != MaterialPlanAxisItemCls.ClassNr) return;

			// Antiduplicitní tabulka:
			Hashtable scanGIdHt = new Hashtable();
			Hashtable compGIdHt = new Hashtable();

			// Které GIDy (výdejka z osy S) budu zpracovávat?  Vložit první výdejku (výdej do poptávky):
			Queue<GID> nextReq = new Queue<GID>();
			nextReq.Enqueue(axisGId);
			while (nextReq.Count > 0)
			{
				// Vyzvedneme si číslo výdejky z osy S, pro které budeme hledat jeho výrobní příjmy (příjmy z výroby polotovarů):
				GID axisRqsGId = nextReq.Dequeue();

				// Najdu předchůdce této výdejky, a z nich si vyberu jen sekundární vztahy typu "Příjemka z výroby do výdejky"
				List<PlanningItemRelationCls> relRcpList = this.AxisHeap.FindRelationPrevIDData(axisRqsGId,
					delegate(PlanningItemRelationCls rel)
					{ return (rel.Relation == PlanningItemRelationType.ProductReceiptToEnquiry || rel.Relation == PlanningItemRelationType.ProductReceiptToNextStructure); } );

				// Nalezené příjemky představují příjem z výroby, a je pro ně třeba založit nové řádky, které popisují výrobu daného dílce:
				foreach (PlanningItemRelationCls relRcpItem in relRcpList)
				{
					// Duplicita čísla záznamu osy S pro příjemku => skip:
					if (scanGIdHt.ContainsKey(relRcpItem.ItemGIDNext)) continue;
					scanGIdHt.Add(relRcpItem.ItemGIDNext, null);

					// Pro daný záznam o příjmu dílce z výroby na sklad založí řádek do grafu, vloží do něj elementy za jednotlivé operace,
					// najde komponenty (záznamy osy S o výdeji do komponent) a vloží je do ref seznamu axisSubLst:
					List<GID> axisSubLst = new List<GID>();
					this._RowAxisSAddSubTaskOne(axisGId, relRcpItem, ref parentTime, ref subRowList, ref axisSubLst);

					// Výdeje komponent do tohoto dílce si vložím do zásobníku výdejek pro prozkoumání v příštím kole:
					foreach (GID axisSubRqs in axisSubLst)
					{
						if (compGIdHt.ContainsKey(axisSubRqs)) continue;   // Duplicita => skip
						compGIdHt.Add(axisSubRqs, null);                   // Antiduplicita.
						nextReq.Enqueue(axisSubRqs);                       // Osa S : výdejka do komponenty - její GID do zásobníku výdejek.
					}
					// Dodělám si příjemky z výroby v jedné úrovni (seznam vztahů relRcpList), 
				}	//  a až je dodělám všechny tak z téhle smyčky vypadnu 
			}		//  a půjdu hledat příjmy pro další výdejky komponent, které jsem si nastřádal do zásobníku nextReq.
		}
		/// <summary>
		/// Najde rozpad dílce na výrobní operace, pro dílec založí řádek do grafu osy S, jednotlivé operace do něj vloží jako elementy,
		/// a po cestě najde výdejky komponent do této výroby.
		/// </summary>
		/// <param name="axisGId">main záznam osy S, jeho GID</param>
		/// <param name="relRcpItem">Záznam o vztahu o příjmu z finální operace výroby dílce na osu S</param>
		/// <param name="parentTime">Čas nadřízeného záznamu, do něj se přidávají časy z podřízených operací</param>
		/// <param name="subRowList">Ref soupis řádků grafu, sem jeden řádek přidám.</param>
		/// <param name="axisSubLst">Ref/Out soupis výdejek do komponent pro tento jeden rozpad. Může obsahovat duplicity, vyšší úroveň si je ohlídá.</param>
		private void _RowAxisSAddSubTaskOne(GID axisGId, PlanningItemRelationCls relRcpItem, ref VisualTaskTimeCls parentTime, ref List<IDataRow> subRowList, ref List<GID> axisSubLst)
		{
			// 1. Najít task, který vstupuje do příjemky relRcpItem.ItemGIDPrev (vztah relRcpItem spojuje záznam o příjmu na osu a návazný výdej někam jinam, ale nemluví se o tasku)
			GID taskLastGId = this.AxisHeap.FindTaskGIdForReceipt(relRcpItem.ItemGIDPrev);
			if (taskLastGId.IsEmpty) return;        // Příjemka na sklad se neváže k žádné výrobě

			// 2. Založím řádek pro graf za příjemku na osu S, která představuje příjem z výroby daného dílce:
			MaterialPlanAxisItemCls axisTask = this._FindItemAxisS(relRcpItem.ItemGIDPrev);               // Podle GIDu osy S najdu záznam osy o příjmu z výroby
			if (axisTask == null) return;
			PlanningVisualDataRowCls rowTask = this.Parent.VisualGetDataRowForAxisSGanttSub(axisTask);    // Ze záznamu osy S vytvořím řádek grafu
			if (rowTask == null) return;            // Z nějakých důvodů jsem nevytvořil řádek.
			rowTask.ParentGId = axisGId;            // Signalizovat parent GID pro tento podřízený řádek
			rowTask.AllElementsLoaded = true;       // Všechny elementy jsou načteny (tedy, budou za chvilku)
			subRowList.Add(rowTask);                // Řádek do výstupu, odkud se převezme do grafu

			// 3. Najít sadu záznamů, které tvoří posloupnost operací, počínaje operací, která jde do příjemky:
			List<PlanningItemRelationCls> componentList = new List<PlanningItemRelationCls>();
			List<GID> taskList = this.AxisHeap.FindTaskGIdInnerList(taskLastGId, TimeRange.TimeDirection.ToHistory, ref componentList);

			// 4. Pro každou operaci založím element = najdu task a podle něj zavedu element:
			foreach (GID taskGId in taskList)
				this._RowAxisSAddSubElement(rowTask, taskGId, ref parentTime);

			// 4. Seznam výdejek do komponent componentList pošlu ven:
			foreach (PlanningItemRelationCls compRelItem in componentList)
				axisSubLst.Add(compRelItem.ItemGIDPrev);   // Záznam o vztahu má charakter AxisToTask, tedy výdej z osy je ItemPrev.
		}
		/// <summary>
		/// Do grafického řádku vloží jeden element za jeden task.
		/// Časy tohoto tasku přičte k parentTime
		/// </summary>
		/// <param name="rowTask"></param>
		/// <param name="taskGId"></param>
		/// <param name="parentTime"></param>
		private void _RowAxisSAddSubElement(PlanningVisualDataRowCls rowTask, GID taskGId, ref VisualTaskTimeCls parentTime)
		{
			CapacityPlanWorkItemCls task = this._FindItemTaskC(taskGId);
			if (task == null) return;

			parentTime.AddTimeFromTask(task);

			PlanningVisualDataElementCls element = new PlanningVisualDataElementCls(task.TaskGID, rowTask.GId, GID.Empty, task.TimeWork, DataElementEditingMode.FixedItem);
			element.SetElementProperty(GraphElementLayerType.ItemLayer, DataElementEditingMode.Editable, GraphElementVisualType.Type1, GraphElementShapeType.Hexagon);
			element.SetColor(Color.FromArgb(178, 143, 27));

			// Můj čas úkolu:
			VisualTaskTimeCls thisTime = VisualTaskTimeCls.CreateFromEnd(task.TimeWork.End);
			thisTime.AddTimeFromTask(task);
			this._ElementSetFragment(element, thisTime);

			this.Parent.VisualSetElementTextForTaskCGantt(task, element);
			rowTask.ElementList.Add(element);
		}


		#region GENERÁTORY ELEMENTŮ DO ŘÁDKŮ ZA OSU S (TVAR, BAREVNOST, TEXTOVÉ POPISKY)
		/// <summary>
		/// Do vizuálního řádku přidá element za daný záznam osy S a daný čas parentTime.
		/// </summary>
		/// <param name="visualRow">Vizuální řádek</param>
		/// <param name="axisGId">GID záznamu osy S</param>
		/// <param name="parentTime"></param>
		private void _RowAxisSAddElementMain(PlanningVisualDataRowCls visualRow, GID axisGId, VisualTaskTimeCls parentTime)
		{
			this._RowAxisSAddElementMain(visualRow, this._FindItemAxisS(axisGId), parentTime);
		}
		/// <summary>
		/// Do vizuálního řádku přidá element za daný záznam osy S a daný čas parentTime.
		/// </summary>
		/// <param name="visualRow">Vizuální řádek</param>
		/// <param name="axis">Data osy S</param>
		/// <param name="parentTime"></param>
		private void _RowAxisSAddElementMain(PlanningVisualDataRowCls visualRow, MaterialPlanAxisItemCls axis, VisualTaskTimeCls parentTime)
		{
			if (axis == null) return;

			PlanningVisualDataElementCls element = new PlanningVisualDataElementCls(axis.AxisGID, axis.AxisGID, GID.Empty, parentTime.TimeReal, DataElementEditingMode.Editable);
			element.SetElementProperty(GraphElementLayerType.ItemLayer, DataElementEditingMode.Editable, GraphElementVisualType.Type1, GraphElementShapeType.Rectangle); // Hexagon);
			element.SetColor(Color.FromArgb(0, 66, 218));
			this._ElementSetFragment(element, parentTime);
			this.Parent.VisualSetElementTextForAxisSGantt(axis, element);
			visualRow.ElementList.Add(element);
		}
		/// <summary>
		/// Do vizuálního řádku přidá element za daný task.
		/// </summary>
		/// <param name="visualRow">Vizuální řádek</param>
		/// <param name="taskGId">GID tasku</param>
		/// <param name="timeRange"></param>
		private void _RowAxisSAddElementTask(PlanningVisualDataRowCls visualRow, GID taskGId, ref VisualTaskTimeCls parentTime)
		{
			this._RowAxisSAddElementTask(visualRow, this._FindItemTaskC(taskGId), ref parentTime);
		}
		/// <summary>
		/// Do vizuálního řádku přidá element za daný task.
		/// </summary>
		/// <param name="visualRow">Vizuální řádek</param>
		/// <param name="task">Data tasku</param>
		/// <param name="timeRange"></param>
		private void _RowAxisSAddElementTask(PlanningVisualDataRowCls visualRow, CapacityPlanWorkItemCls task, ref VisualTaskTimeCls parentTime)
		{
			if (task == null) return;

			parentTime.AddTimeFromTask(task);

			// Můj čas úkolu:
			VisualTaskTimeCls thisTime = VisualTaskTimeCls.CreateFromEnd(task.TimeWork.End);
			thisTime.AddTimeFromTask(task);

			PlanningVisualDataElementCls element = new PlanningVisualDataElementCls(task.TaskGID, visualRow.GId, GID.Empty, task.TimeWork, DataElementEditingMode.FixedItem);
			element.SetElementProperty(GraphElementLayerType.ItemLayer, DataElementEditingMode.Editable, GraphElementVisualType.Type1, GraphElementShapeType.Hexagon);
			element.SetColor(Color.FromArgb(178, 143, 27));
			this._ElementSetFragment(element, thisTime);
			this.Parent.VisualSetElementTextForTaskCGantt(task, element);
			visualRow.ElementList.Add(element);
		}
		/// <summary>
		/// Do elementu vepíše časové fragmenty z dodaného časosběrného objektu
		/// </summary>
		/// <param name="element"></param>
		/// <param name="taskTime"></param>
		private void _ElementSetFragment(PlanningVisualDataElementCls element, VisualTaskTimeCls taskTime)
		{
			element.Fragments = new float[4];
			element.Fragments[0] = taskTime.SecondsTMC;
			element.Fragments[1] = taskTime.SecondsTBC;
			element.Fragments[2] = taskTime.SecondsTAC;
			element.Fragments[3] = taskTime.SecondsTEC;
		}
		#endregion
		#endregion

		#region OLDIES   -   SESTAVENÍ PODŘÍZENÝCH ŘÁDKŮ PRO ZÁZNAMY OSY S: PODŘÍZENÉ DÍLCE, JEJICH VÝROBA (OPERACE = ELEMENTY)
		/// <summary>
		/// Metoda zjistí, zda do daného řádku osy S vstupuje přinejmenším jeden podřízený task,
		/// tedy zda řádek bude mít zobrazenou ikonku [+].
		/// </summary>
		/// <param name="parentRowGId"></param>
		/// <returns></returns>
		private bool _RowAxisSContainSubRows(GID parentRowGId)
		{
			// Najdu podřízené Tasky, a současně určím z podřízených záznamů jejich časový interval (meziskladových převodů, doba výroby):
			VisualTaskTimeCls parentTime = VisualTaskTimeCls.Empty;
			List<PlanningItemRelationCls> subTaskList = this._RowAxisSFindSubTasks(parentRowGId, ref parentTime, true);

			return (subTaskList.Count > 0);
		}
		/// <summary>
		/// Metoda načte a zajistí vrácení řádků osy S, které jsou podřízené k požadovanému řádku.
		/// </summary>
		/// <param name="request"></param>
		private void _ReadRowsSubPlanAxisS(DataSourceRequest request)
		{ }
		/// <summary>
		/// Metoda sestaví a vrátí soupis řádků, které představují podřízené pracovní úkoly 
		/// k cílovému záznamu osy S, který je výdejem (do poptávky, do komponenty).
		/// Metoda dostává klíč cílového záznamu osy, datum tohoto výdeje.
		/// Přes referenci plni datový interval (plní počátek pracovního výkonu) 
		/// a přidává položky jednotlivých vizuálních řádků do souhrnu podřízených řádků.
		/// </summary>
		/// <remarks>
		/// Metoda nejprve dohledává skladové převody, které předcházejí danému výdeji, a z nich střádá čas těchto skladových přesunů.
		/// Současně vyhledává tasky, které do těchto skladových přesunů vstupují = výroba výsledného dílce (může jich být více).
		/// Pro každý tento finální task zakládá nový řádek do soupisu řádků.
		/// Pro každý tento finální task dohledává jeho předchozí vztahy, a pokud jde o TaskInner, pak zakládá další elementy do téhož řádku.
		/// Pokud jde o vztah Výdej komponenty (AxisToTask), pak rekurzivně spouští tuto metodu = dohledávám časy převodu, a zakládám řádky pro podřízené dílce.
		/// </remarks>
		/// <param name="parentRowGId">Parent řádek, vždy jeden pro všechny sub levels (sestavuji jednu úroveň podřízených záznam, všechny vážu k jednomu parentu = výdej do poptávky).</param>
		/// <param name="axisGId">Cílový záznam osy S, který je výdejem (výsledek do poptávky, anebo komponenta do nadřízené výroby). Tento GID slouží jako klíč pro hledání podřízených.</param>
		/// <param name="timeRange"></param>
		/// <param name="subDict"></param>
		private void _RowAxisSAddSubTasks_Old(GID parentRowGId, GID axisGId, ref VisualTaskTimeCls parentTime, ref Dictionary<GID, IDataRow> subDict)
		{
			// Na vstupu není záznam materiálové osy: odmítneme jej:
			if (axisGId.ClassNumber != MaterialPlanAxisItemCls.ClassNr) return;
			// Na vstupu je záznam osy, který už máme zpracovaný: vynecháme jej:
			if (subDict.ContainsKey(axisGId)) return;

			// Najdu podřízené Tasky, a současně určím z podřízených záznamů jejich časový interval (meziskladových převodů, doba výroby):
			List<PlanningItemRelationCls> subTaskList = this._RowAxisSFindSubTasks(axisGId, ref parentTime, false);

			// Pro podřízené tasky zavolám jejich rozpad. Ten založí nový řádek do grafu (do subDict).
			// Do tohoto řádku vloží elementy za každou operaci.
			// Současně najde soupis záznamů osy, které představují výdeje komponent do tohoto řádku.
			// Tyto výdeje komponent zahájí novou rekurzi zdejší metody (tj. _ReadRowsSubPlanAxisS())...
			foreach (PlanningItemRelationCls taskAxisRel in subTaskList)
			{
				this._RowAxisSCreateSubTask(parentRowGId, taskAxisRel, ref parentTime, ref subDict);
			}
		}
		/// <summary>
		/// Metoda postupně prohledá předchůdce daného záznamu (jde o GID osy S).
		/// Jednak, pokud předchůdce je osa S, pak rozšiřuje předaný časový interval o interval na této ose.
		/// Současně hledá její podřízené záznamy a rekurzivně je zpracovává (dohledává tak opakované meziskladové přesuny a vnitroskladové balance).
		/// A pokud je podřízeným záznamem výstup finálu z tasku, pak vztah z Tasku do osy S zařadí do výstupu.
		/// Tento podřízený task se stane další položkou v soupisu řádků, bude se mapovat samostatně.
		/// </summary>
		/// <param name="axisGId">GID záznam osy S, pro který hledám podřízené</param>
		/// <param name="timeRange"></param>
		/// <param name="endOnFirst">Skončit po prvním nalezeném tasku (určuje se příznak Jsou/Nejsou, a první task mi jako příznak stačí)</param>
		/// <returns></returns>
		private List<PlanningItemRelationCls> _RowAxisSFindSubTasks(GID axisGId, ref VisualTaskTimeCls parentTime, bool endOnFirst)
		{
			Dictionary<GID, PlanningItemRelationCls> subTaskDict = new Dictionary<GID, PlanningItemRelationCls>();

			List<PlanningItemRelationCls> relList = this.AxisHeap.FindRelationPrevIDData(axisGId);
			if (relList == null || relList.Count == 0) return new List<PlanningItemRelationCls>();

			Hashtable scanGIds = new Hashtable();
			scanGIds.Add(axisGId, null);                   // AntiDuplicit hashtable

			for (int r = 0; r < relList.Count; r++)
			{	// Musím takhle, protože do soupisu průběžně přidávám další a další vztahy, jak postupuje převod mezi sklady do hloubky:
				PlanningItemRelationCls relItem = relList[r];
				GID prevGID = relItem.ItemGIDPrev;
				switch (relItem.Relation)
				{
					case PlanningItemRelationType.AxisBalance:
					case PlanningItemRelationType.AxisTransfer:
						// Jde o přesun mezi příjmem a výdejem, v rámci skladu nebo mezi sklady. Ale není to výroba.
						this._RowAxisSEnlargeTime(ref parentTime, prevGID);
						// Předchůdce (relItem.ItemGIDPrev) je tedy záznam osy, i on může mít své předchůdce.
						// Pokud jsme tohoto předchůdce dosud nemapovali, přidáme jej do soupisu k prohledání:
						if (!scanGIds.ContainsKey(prevGID))
						{	// Najdeme všechny předchůdce aktuálního předchůdce, a zařadíme tyto položky vztahů do aktuálního seznamu:
							List<PlanningItemRelationCls> relListSub = this.AxisHeap.FindRelationPrevIDData(prevGID);
							if (relListSub != null)
								relList.AddRange(relListSub);
							scanGIds.Add(prevGID, null);
						}
						break;

					case PlanningItemRelationType.AxisToTask:
					case PlanningItemRelationType.TaskInner:
					case PlanningItemRelationType.TaskTransfer:
						// Převod do tasku: to tady nemá co dělat:
						Throw.SysError(MessageInfo.Get("Chyba: metoda DataVisual._ReadRowsSubPlanAxisSFindSubTasks() narazila na vztah %0, který typicky míří do Tasku, ale metoda má být volána pro vztahy do osy Axis. GID vstupního záznamu=%1", relItem.Relation.ToString(), axisGId.ToString()));
						break;

					case PlanningItemRelationType.TaskToAxis:
						// Příjem z tasku na osu. 
						// Zde by byla šance ignorovat příjmy vedlejších produktů, ale nechávám na systému, zda je použije pro zajištění.
						//   Pokud ano tak je zobrazím, ale jejich výroba bude zobrazena jen do té operace, odkud vedlejší produkt odeberu. Což je správně.
						// relItem.ItemGIDPrev je tedy GID předešlého záznamu = Task, dám tento GID do soupisu výsledků.
						if (!subTaskDict.ContainsKey(prevGID))
							// Nonduplicitně:
							subTaskDict.Add(prevGID, relItem);
						break;

				}
				// Pokud mi stačí první task, skončím:
				if (endOnFirst && subTaskDict.Count > 0) break;
			}
			return new List<PlanningItemRelationCls>(subTaskDict.Values);
		}
		/// <summary>
		/// Pro daný úkol založí nový vizuální řádek, vepíše do něj elementy za každou operaci
		/// a vyhledá záznamy, které do této výroby (do všech operací) vstupují.
		/// </summary>
		/// <param name="parentRowGId">Parent řádek, vždy jeden pro všechny sub levels (sestavuji jednu úroveň podřízených záznam, všechny vážu k jednomu parentu = výdej do poptávky).</param>
		/// <param name="taskAxisRel">Záznam o vztahu typu TaskToAxis, který převádí výrobek z tasku na osu. Obsahuje tedy jak osu (jejíž GID je GIDem nového řádku), tak GID tasku, jehož operaci a všechny předchozí budeme vkládat do elementů.</param>
		/// <param name="timeRange"></param>
		/// <param name="subDict"></param>
		private void _RowAxisSCreateSubTask(GID parentRowGId, PlanningItemRelationCls taskAxisRel, ref VisualTaskTimeCls parentTime, ref Dictionary<GID, IDataRow> subDict)
		{
			GID rowGId = taskAxisRel.ItemGIDNext;      // GID záznamu osy, který je příjmem z výroby
			if (subDict.ContainsKey(rowGId)) return;   // AntiLooping

			MaterialPlanAxisItemCls axisFinal = this._FindItemAxisS(rowGId);        // Podle GIDu osy najdu záznam osy
			PlanningVisualDataRowCls rowTask = this.Parent.VisualGetDataRowForAxisSGanttSub(axisFinal);  // Ze záznamu osy vytvořím řádek grafu
			if (rowTask == null) return;               // Z nějakých důvodů jsem nevytvořil řádek.

			rowTask.ParentGId = parentRowGId;          // Řádek navěsím na parent řádek (bude z něj jeho podřízený).
			subDict.Add(rowGId, rowTask);              // Řádek přidám do soupisu, klíčem je GID záznamu osy S

			// Budu prohledávat zpětně všechny Inner předchůdce daného tasku:
			//   a za záznamy typu Task ve vztahu Inner (tzn. operace jednoho dílce) budu zakládat elementy do řádku.
			//   Vztahy o výdeji komponent do tasku budu střádat.
			//   Vztahy o příjmu z tasku budu ignorovat (jde o vedlejší produkty, ty tady nemapuji).
			Dictionary<GID, PlanningItemRelationCls> subAxisDict = new Dictionary<GID, PlanningItemRelationCls>();
			GID taskGId = taskAxisRel.ItemGIDPrev;     // GID výchozího tasku (tzn. poslední v řadě operací), tím začneme:
			while (taskGId.NotEmpty)
			{
				// Do vizuálního řádku přidám element za aktuální task:
				this._RowAxisSAddElementTask(rowTask, taskGId, ref parentTime);
				GID nextGId = GID.Empty;               // GID příští operace

				// Předchůdci tasku (předchozí operace, vstupující komponenty):
				List<PlanningItemRelationCls> relList = this.AxisHeap.FindRelationPrevIDData(taskGId);
				if (relList == null || relList.Count == 0) break;

				// Tak koho tu máme, kdo je předchůdcem aktuálního tasku:
				foreach (PlanningItemRelationCls relItem in relList)
				{
					switch (relItem.Relation)
					{
						case PlanningItemRelationType.TaskInner:
							// Vztah mezi sousedními operacemi: příští smyčka zpracuje sousední (tzn. časově předešlou) operaci:
							nextGId = relItem.ItemGIDPrev;
							break;
						case PlanningItemRelationType.AxisToTask:
							// Komponenta:
							if (!subAxisDict.ContainsKey(relItem.ItemGIDPrev))
								subAxisDict.Add(relItem.ItemGIDPrev, relItem);
							break;
					}
				}

				// V následujícím cyklu zpracujeme příští operaci:
				taskGId = nextGId;
			}
			// Vlastnosti řádku:
			rowTask.AllElementsLoaded = true;
			rowTask.HasSubNodes = false;

			// Pro všechny komponenty, vstupující do daného dílce, zavolám rekurzivně funkci rozpadu výdeje (z osy do komponenty):
			foreach (PlanningItemRelationCls relStruct in subAxisDict.Values)
			{
				// Podle GIDu komponenty najdu záznam osy o výdeji komponenty do výroby:
				MaterialPlanAxisItemCls axisStruct = this._FindItemAxisS(relStruct.ItemGIDPrev);
				if (axisStruct == null) continue;

				// Právě tady začíná rekurze, vytvářím nově proměnné:
				VisualTaskTimeCls parentTimeStruct = VisualTaskTimeCls.CreateFromEnd(axisStruct.DatePlanEnd);
				this._RowAxisSAddSubTasks_Old(parentRowGId, axisStruct.AxisGID, ref parentTimeStruct, ref subDict);

				// Vrátil jsem se z rekurze do podřízené operace, kde došlo k vygenerování časového intervalu této podřízené operace.
				// Čas z podřízené operace přidám do aktuální parent operace:
				parentTime.AddTimeFromTime(parentTimeStruct);
			}
		}
		#endregion
		#region PODPORA - HLEDÁNÍ DAT OSY S A TASKU PODLE GIDU

		/// <summary>
		/// Rozšíří předaný časový interval o čas daného záznamu osy S.
		/// Rozšiřuje pouze segment TimeTMS = čas skladového přesunu.
		/// </summary>
		/// <param name="timeRange"></param>
		/// <param name="axisS"></param>
		private void _RowAxisSEnlargeTime(ref VisualTaskTimeCls parentTime, GID axisS)
		{
			//MaterialPlanAxisItemCls axis = this._RowAxisSFindItem(axisS);
			//if (axis != null)
			//    parentTime.ExpandTimeStock(axis.DatePlanTime);
		}
		/// <summary>
		/// Najde a vrátí záznam osy S pro daný GID. Předem kontroluje NotEmpty a číslo třídy GIDu.
		/// Pokud neodpovídá GID nebo třída, anebo záznam není nalezen, vrací null. Nevyhodí chybu.
		/// </summary>
		/// <param name="axisS"></param>
		/// <returns></returns>
		private MaterialPlanAxisItemCls _FindItemAxisS(GID axisS)
		{
			if (axisS.NotEmpty && axisS.ClassNumber == MaterialPlanAxisItemCls.ClassNr)
				return this.AxisHeap.FindAxisSItem(axisS.RecordNumber);
			return null;
		}
		/// <summary>
		/// Najde a vrátí záznam kapacitního tasku pro daný GID. Předem kontroluje NotEmpty a číslo třídy GIDu.
		/// Pokud neodpovídá GID nebo třída, anebo záznam není nalezen, vrací null. Nevyhodí chybu.
		/// </summary>
		/// <param name="taskC"></param>
		/// <returns></returns>
		private CapacityPlanWorkItemCls _FindItemTaskC(GID taskC)
		{
			if (taskC.NotEmpty && taskC.ClassNumber == CapacityPlanWorkItemCls.ClassNr)
				return this.AxisHeap.FindTaskCItem(taskC.RecordNumber);
			return null;
		}
		#endregion
		#region ČTENÍ DAT Z PROCESU DO GUI - POLOŽKY GRAFŮ
		/// <summary>
		/// Načtení elementů pro daný řádek a období
		/// </summary>
		/// <param name="request"></param>
		private void _ReadElements(DataSourceRequest request)
		{
			switch (request.RequestRowGId.ClassNumber)
			{
				case PlanUnitCCls.ClassNr:
					this._ReadElementsPlanUnitC(request);
					break;
			}
		}
		/// <summary>
		/// Načtení zcela konkrétních elementů, jsou zadané jejich pozicí (plánovací jednotka, WorkUnit)
		/// </summary>
		/// <param name="request"></param>
		private void _ReadElementsMap(DataSourceRequest request)
		{
			List<IDataElement> result = new List<IDataElement>();
			foreach (DataPointerStr dataPointer in request.RequestElementsMap)
			{
				switch (dataPointer.Row.ClassNumber)
				{
					case PlanUnitCCls.ClassNr:
						this._ReadElementsOnePlanUnitC(dataPointer, ref result);
						break;
				}
			}
			request.ResultElements = result;
		}
		/// <summary>
		/// Načtení elementů pro daný řádek a období, pro řádek třídy Kapacitní plánovací jednotka
		/// </summary>
		/// <param name="request"></param>
		private void _ReadElementsPlanUnitC(DataSourceRequest request)
		{
			switch (request.RequestedGraphMode)
			{
				case RowGraphMode.CapacityUtilization:
					_ReadElementsPlanUnitCGraphCU(request);
					break;
				case RowGraphMode.TaskCapacityLink:
					_ReadElementsPlanUnitCGraphTL(request);
					break;
			}
		}
		/// <summary>
		/// Metoda načte jeden element grafu a přidá jej do seznamu.
		/// </summary>
		/// <param name="dataPointer"></param>
		/// <param name="result"></param>
		private void _ReadElementsOnePlanUnitC(DataPointerStr dataPointer, ref List<IDataElement> result)
		{
			if (dataPointer.Element.ClassNumber == Constants.ClassNumberWork)
			{
				WorkUnitCls workUnit = this.AxisHeap.FindIWorkItem(dataPointer.Element.RecordNumber);
				if (workUnit != null)
				{
					PlanningVisualDataElementCls element = this._ReadElementsCreateTaskLinkFromWorkUnit(workUnit);
					result.Add(element);
				}
			}
		}
		/// <summary>
		/// Načtení dat elementů pro kapacitní jednotku a graf Využití kapacit
		/// </summary>
		/// <param name="request"></param>
		private void _ReadElementsPlanUnitCGraphCU(DataSourceRequest request)
		{
			CapacityUnitCls unit = this.CapacityData.GetCapacityUnit(request.RequestRowGId.RecordNumber);
			if (unit == null) return;
			List<CapacityLevelItemCls> levelList = unit.GetCapacityLevels(request.RequestedTimeRange);
			foreach (CapacityLevelItemCls level in levelList)
			{
				float utilizationRatio = (float)(level.CapacityTotal > 0M ? level.CapacityUsed / level.CapacityTotal : 0M);
				PlanningVisualDataElementCls element = new PlanningVisualDataElementCls(new GID(PlanUnitCClCls.ClassNr, level.LevelID), request.RequestRowGId, GID.Empty, level.Time);
				element.SetElementProperty(GraphElementLayerType.ItemLayer, DataElementEditingMode.FixedItem, GraphElementVisualType.Type1, 1F, utilizationRatio);
				element.SetElementProperty(GraphElementShapeType.CapacityUtilization);
				element.SetColor(Color.LightYellow);
				request.ResultElements.Add(element);
			}
		}
		/// <summary>
		/// Načtení dat elementů pro kapacitní jednotku a graf Vztah tasku a kapacitní jednotky
		/// </summary>
		/// <param name="request"></param>
		private void _ReadElementsPlanUnitCGraphTL(DataSourceRequest request)
		{
			CapacityUnitCls unit = this.CapacityData.GetCapacityUnit(request.RequestRowGId.RecordNumber);
			if (unit == null) return;
			List<CapacityLevelItemCls> levelList = unit.GetCapacityLevels(request.RequestedTimeRange);
			foreach (CapacityLevelItemCls level in levelList)
			{
				// 1. Element za stav kapacit:
				PlanningVisualDataElementCls elementL = new PlanningVisualDataElementCls(new GID(PlanUnitCClCls.ClassNr, level.LevelID), request.RequestRowGId, GID.Empty, level.Time);
				elementL.SetElementProperty(GraphElementLayerType.SubLayer, DataElementEditingMode.FixedItem, GraphElementVisualType.Type1);
				elementL.SetElementProperty(GraphElementShapeType.Rectangle);
				elementL.SetColor(Color.LightYellow);
				request.ResultElements.Add(elementL);

				// 2. Element za každý jednotlivý Job:
				foreach (CapacityDeskCls desk in level.CapacityDesk)
				{
					foreach (CapacityJobItemCls job in desk.JobList)
					{
						WorkUnitCls workUnit = (WorkUnitCls)job.WorkUnit;
						PlanningVisualDataElementCls elementJ = _ReadElementsCreateTaskLinkFromWorkUnit(workUnit);
						request.ResultElements.Add(elementJ);
					}
				}
			}
		}
		/// <summary>
		/// Vygeneruje a vrátí vizuální element
		/// </summary>
		/// <param name="iWorkUnitData"></param>
		/// <returns></returns>
		private PlanningVisualDataElementCls _ReadElementsCreateTaskLinkFromWorkUnit(WorkUnitCls workUnit)
		{
			Random rnd = new Random();
			DataElementEditingMode moveMode = DataElementEditingMode.Editable;
			PlanningVisualDataElementCls elementJ = new PlanningVisualDataElementCls(
				new GID(Constants.ClassNumberWork, workUnit.WorkID),
				new GID(PlanUnitCCls.ClassNr, workUnit.UnitID),
				new GID(Constants.ClassNumberTask, workUnit.TaskID), 
				workUnit.WorkTime, 
				moveMode);
			DataElementEditingMode editMode = DataElementEditingMode.Movable | DataElementEditingMode.ShiftItemsOnSameThread | DataElementEditingMode.AffinityToInitialTime;
			elementJ.SetElementProperty(GraphElementLayerType.ItemLayer, editMode, GraphElementVisualType.Type2);
			elementJ.SetElementProperty(GraphElementShapeType.Rectangle);
			Color col = Color.FromArgb(Color.LightSkyBlue.R, Color.LightSkyBlue.G, rnd.Next(60, 255));
			elementJ.SetColor(col);
			this.Parent.VisualSetElementTextForTaskCLink(workUnit, elementJ);
			return elementJ;
		}
		#endregion
		#region ČTENÍ VZTAHŮ MEZI ELEMENTY PRO ZOBRAZENÍ SÍTĚ VZTAHŮ
		/// <summary>
		/// Metoda najde pro daný element jeho vztahy na sousední elementy 
		/// a sestavený soupis uloží do request.ResultRelations = new List...()
		/// </summary>
		/// <param name="request"></param>
		private void _CreateRelations(DataSourceRequest request)
		{
			int workID = request.RequestElementGId.RecordNumber;          // Číslo WorkID
			int workCls = Constants.ClassNumberWork;
			int reqsCls = request.RequestElementGId.ClassNumber;
			List<DataRelation> result = this.Parent.GetWorkUnitRelations(workID, true, true);
			request.ResultRelations = result;
		}
		#endregion
		#region ZÁPIS DAT Z VIZUÁLNÍ VRSTVY DO PROCESU
		/// <summary>
		/// Do plánovacího procesu vloží změnu definovanou v argumentu
		/// </summary>
		/// <param name="request">Požadavek na zmenu</param>
		private void _SetChange(DataSourceRequest request)
		{
		}
		#endregion

		#region PODPŮRNÉ METODY
		/// <summary>
		/// Vygeneruje barvu pro element grafu.
		/// </summary>
		/// <param name="graphType">Typ grafu</param>
		/// <param name="visualType">Typ elementu. Jde o hodnotu PlanningVisualDataElementCls.ElementVisualType</param>
		/// <returns>Barva</returns>
		public Color GetColorForElement(RowGraphMode graphType, GraphElementVisualType visualType)
		{
			switch (graphType)
			{
				case RowGraphMode.Gantt:
				case RowGraphMode.TaskCapacityLink:
					switch (visualType)
					{	// Tyto barvy odpovídají druhu práce / typu položky plánu.
						// Jejich využití je na dohodě mezi generátorem elementů, jaký vizuální typ do elementu vepíše,
						// a touto metodou, jakou barvu k tomuto typu přiřadí.
						case GraphElementVisualType.Type1:
							// Reálné výrobní příkazy:
							return Color.DarkSlateBlue;             // Color color = Color.FromArgb(72, 61, 139)
						case GraphElementVisualType.Type2:
							// Operace skutečné, navázané na výrobní příkaz:
							return Color.FromArgb(106, 93, 220);
						case GraphElementVisualType.Type3:
							// Rezerva:
							return Color.DarkRed;
						case GraphElementVisualType.Type4:
							// Návrhy na výrobní příkazy:
							return Color.Olive;                     // Color color = Color.FromArgb(128, 128, 0)
						case GraphElementVisualType.Type5:
							// Operace plánované:
							return Color.FromArgb(190, 190, 68);
						case GraphElementVisualType.Type6:
							return Color.DarkSalmon;
						case GraphElementVisualType.Type7:
							return Color.DarkCyan;
						case GraphElementVisualType.Type8:
							return Color.DarkGreen;
						case GraphElementVisualType.Type9:
							return Color.DarkOrange;
					}
					return Color.DarkBlue;

				case RowGraphMode.CapacityUtilization:
					switch (visualType)
					{	// Tyto barvy odpovídají směnám:
						// Jejich využití je na dohodě mezi generátorem elementů, jaký vizuální typ do elementu vepíše,
						// a touto metodou, jakou barvu k tomuto typu přiřadí.
						case GraphElementVisualType.Type1:
							return Color.FromArgb(128, 220, 255, 220);
						case GraphElementVisualType.Type2:
							return Color.FromArgb(128, 220, 255, 255);
						case GraphElementVisualType.Type3:
							return Color.FromArgb(128, 255, 220, 255);
						case GraphElementVisualType.Type4:
							return Color.FromArgb(128, 255, 255, 220);
						case GraphElementVisualType.Type5:
							return Color.FromArgb(128, 255, 255, 255);
						case GraphElementVisualType.Type6:
							return Color.FromArgb(128, 192, 220, 220);
						case GraphElementVisualType.Type7:
							return Color.DarkCyan;
						case GraphElementVisualType.Type8:
							return Color.DarkGreen;
						case GraphElementVisualType.Type9:
							return Color.DarkOrange;
					}
					return Color.FromArgb(128, 192, 255, 192);
			}
			return Color.DarkBlue;
		}
		/// <summary>
		/// Metoda, která má za úkol vrátit soupis GID vedlejších prvků k danému prvku.
		/// Tato metoda se použije pouze v procesu editace, kdy editovaný blok dat (sada operací, atd) neobsahuje všechny prvky, na které se odkazuje.
		/// Typicky to nastane v situaci, kdy prvek 30 se odkazuje na následující prvek 40, ale ten neexistuje.
		/// Tato metoda pak musí vrátit GID prvku, který má navazovat na prvek 40 => tj. typicky půjde o prvek 50.
		/// Pokud elementy, vrácené metodou RunRequestXxx(DataSourceRequestType.ElementsRead) vrací kompaktní skupiny dat, kde nejsou díry, 
		/// pak zdejší metoda GetNearbyGIDs() může vždy vracet null.
		/// </summary>
		/// <param name="gID">GID výchozího prvku</param>
		/// <param name="orientation">Směr, v němž hledáme další prvky</param>
		/// <returns>Soupis GIDů, které v daném směru navazují. Může být null.</returns>
		public IEnumerable<GID> GetNearbyGIDs(GID gID, ElementRelationOrientation orientation)
		{
			return null;
		}
		#endregion
	}
	#endregion
	#region CLASS PlanningVisualDataRowCls : jeden řádek s daty
	/// <summary>
	/// Jedna položka, která popisuje záhlaví jednoho řádku libovolného seznamu
	/// </summary>
	public class PlanningVisualDataRowCls : Noris.Schedule.Support.IDataRow
	{
		#region PROPERTY
		/// <summary>
		/// Globální identifikátor tohoto záznamu.
		/// </summary>
		public GID GId { get { return _GId; } }
		/// <summary>
		/// Reference záznamu (zakázky, výrobního příkazu, operace, pracoviště, kapacitní jednotky, ...)
		/// </summary>
		public string Refer { get { return _Refer; } set { _Refer = value; } }
		/// <summary>
		/// Název záznamu (zakázky, výrobního příkazu, operace, pracoviště, kapacitní jednotky, ...)
		/// </summary>
		public string Name { get { return _Name; } set { _Name = value; } }
		/// <summary>
		/// Příznak, zda tento řádek má své pod-uzly (subnodes), tj. můžeme pro něj zobrazit ikonku [+] ve stromu
		/// </summary>
		public bool HasSubNodes { get { return _HasSubNodes; } set { _HasSubNodes = value; } }
		/// <summary>
		/// Druh grafu, který je pro tento řádek zobrazen
		/// </summary>
		public RowGraphMode RowGraph { get { return _RowGraph; } set { _RowGraph = value; } }
		/// <summary>
		/// Násobek obvyklé výšky řádku pro tuto položku.
		/// Default = 1 pro běžné textové řádky.
		/// </summary>
		public float RowHeightMultiplier
		{
			get { return _RowHeightMultiplier; }
			set { _RowHeightMultiplier = value; }
		}
		/// <summary>
		/// Globální identifikátor záznamu parenta.
		/// Typicky se parent nevepisuje: systém typicky funguje tak, že parent je ten objekt, pro který jsou načítány jeho podřízené prvky.
		/// ParentGId lze setovat. Vizuální vrstva jej přebírá po vrácení dat, ale pro svoji další interní práci si uchovává ParentGId jinde.
		/// Vztah na Parenta je tedy řešen na úrovni grafiky, která požádala o načtení podřízených záznamů.
		/// Nicméně je možno při načítání podřízených záznamů rovnou načíst i hlubší podřízené záznamy, a jim pak vyplnit ParentGId na explicitní záznam.
		/// Například: máme načíst všechny operace pro jeden výrobní příkaz.
		/// Načteme 6 operací, a do nich nevyplňujeme ParentGId (což je defaultní chování).
		/// Nad rámec operací se rozhodneme načíst i komponenty operací, tedy podřízené řádky.
		/// Pak pro každou ze 6 operací najdeme její komponenty, načteme je, do každé komponenty vložíme ParentGId = GId její operace, 
		/// a komponenty přidáme do výsledného seznamu dat. Sestavení struktury stromu pak zajistí vizuální vrstva.
		/// Toto je navíc i rekurzivní možnost.
		/// </summary>
		public GID ParentGId { get { return _ParentGId; } set { _ParentGId = value; } }
		/// <summary>
		/// Pole elementů (časové položky grafu), které jsou načítány společně s řádkem.
		/// Ve výchozím stavu je null !
		/// Některé datové typy jsou konstruovány tak, že časová položka je nedílnou součástí řádku 
		/// (např. operace výrobního příkazu je současně řádkem grafu, a současně časovou položkou). 
		/// Pak je vhodné vygenerovat časové položky společně s položkou řádku, a uložit informace do tohoto pole.
		/// Metoda GetRows tak vyřeší načítání jak řádku, tak časových elementů.
		/// Pokud jsou touto metodou načteny všechny časové úseky (např. může existovat jen jeden čas, a ten je uložen do tohoto pole),
		/// pak je třeba nastavit this.AllElementsLoaded = true, a řídící jednotka grafu pak nebude nikdy donačítat data pro širší časové úseky.
		/// Tento mechanismus se nehodí pro objekty, které mají časově široké rozmístění mnoha elementů, typicky stavy kapacit.
		/// </summary>
		public List<IDataElement> ElementList
		{
			get
			{
				if (_ElementList == null)
					_ElementList = new List<IDataElement>();
				return _ElementList;
			}
			set { _ElementList = value; }
		}
		/// <summary>
		/// Příznak, že elementy načtené do pole this.ElementList představují úplnou sadu elementů, 
		/// a pro tento řádek (this) nebude nikdy zapotřebí donačítat elementy pro širší časové pole.
		/// </summary>
		public bool AllElementsLoaded { get { return _AllElementsLoaded; } set { _AllElementsLoaded = value; } }
		/// <summary>
		/// Příznak, že elementy načtené do pole this.ElementList představují úplnou sadu elementů pro určitý (tento) WorkGID.
		/// Víc elementů pro tento WorkGID neexistuje. Typicky se nastavuje na neprázdný WorkGID při iniciaci dat osy S nebo osy C,
		/// kde se načítá jen jeden element pro jeden konkrétní WorkGID.
		/// Zůstává Empty při načítání elementů kapacitní jednotky, kde nelze předem načíst celý WorkList.
		/// </summary>
		public GID WorkGIDAllElementsLoaded { get { return _WorkGIDAllElementsLoaded; } set { _WorkGIDAllElementsLoaded = value; } }
		/// <summary>
		/// Ikona pro tento řádek
		/// </summary>
		public Image Icon
		{
			get
			{
				if (this._Icon == null) this._Icon = DataSourceMfrPlan.GetIconForGId(this.GId);
				return this._Icon;
			}
		}
		/// <summary>
		/// Vrátí text, který se zobrazuje v ToolTipu.
		/// Může vrátit null, pak se za tento objekt nic nezobrazí.
		/// Může vrátit text, obsahující CrLf, pak bude text odřádkován.
		/// </summary>
		public string ToolTipInfo()
		{
			string result = String.Join(": ", new string[] { this.Refer, this.Name });

			// TextNote:
			string eols = "\r\n      ";
			if (this.TextNote != null)
				result += eols + this.TextNote.Replace("\r\n", eols);
			return result;
		}
		#endregion
		#region KONSTRUKTORY A VIZUALIZACE
		public PlanningVisualDataRowCls()
		{
			this._GId = new GID();
			this.Refer = null;
			this.Name = null;
			this.HasSubNodes = false;
			this.TextNote = "";
		}
		public PlanningVisualDataRowCls(GID gid, RowGraphMode rowGraph, string refer, string name)
		{
			this._GId = gid;
			this.RowGraph = rowGraph;
			this.Refer = refer;
			this.Name = name;
			this.HasSubNodes = false;
			this.TextNote = "";
		}
		public PlanningVisualDataRowCls(GID gid, RowGraphMode rowGraph, string refer, string name, float rowHeightMultiplier)
		{
			this._GId = gid;
			this.RowGraph = rowGraph;
			this.Refer = refer;
			this.Name = name;
			this.HasSubNodes = false;
			this.RowHeightMultiplier = rowHeightMultiplier;
			this.TextNote = "";
		}
		public PlanningVisualDataRowCls(GID gid, RowGraphMode rowGraph, string refer, string name, bool hasSubNodes)
		{
			this._GId = gid;
			this.RowGraph = rowGraph;
			this.Refer = refer;
			this.Name = name;
			this.HasSubNodes = hasSubNodes;
			this.TextNote = "";
		}
		public PlanningVisualDataRowCls(GID gid, RowGraphMode rowGraph, string refer, string name, bool hasSubNodes, string description)
		{
			this._GId = gid;
			this.RowGraph = rowGraph;
			this.Refer = refer;
			this.Name = name;
			this.HasSubNodes = hasSubNodes;
			this.TextNote = (String.IsNullOrEmpty(description) ? String.Empty : description);
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return this.GId.ToString() + ":[" + (this.Refer == null ? "" : this.Refer) + ": " + (this.Name == null ? "" : this.Name) + "]";
		}
		#endregion
		#region FIELDS
		private GID _GId;
		private GID _ParentGId;
		private Image _Icon;
		private string _Refer;
		private string _Name;
		private string _Description;
		private bool _HasSubNodes;
		private RowGraphMode _RowGraph;
		private List<IDataElement> _ElementList;
		private bool _AllElementsLoaded;
		private GID _WorkGIDAllElementsLoaded;
		private float _RowHeightMultiplier = 1F;
		/// <summary>
		/// Může obsahovat rozšiřující text, který bude vracet metoda ToolTip.
		/// </summary>
		public string TextNote;
		#endregion
	}
	#endregion
	#region CLASS PlanningVisualDataElementCls : jeden prvek grafu, jeho logická data
	/// <summary>
	/// Jedna položka, která popisuje jednu položku grafu.
	/// Obsahuje informaci o parent GID = řádek, do něhož patří, obsahuje svůj GID = specifikuje svoji třídu, 
	/// obsahuje časový úsek v rámci grafu a další informace o způsobu využití uvedeného času.
	/// Neobsahuje nic grafického ve smyslu rozměrů, pozice, barvy, ikony atd.
	/// </summary>
	public class PlanningVisualDataElementCls : Noris.Schedule.Support.IDataElement
	{
		#region PROPERTY
		/// <summary> { get; }
		/// Globální identifikátor záznamu = tohoto elementu
		/// </summary>
		public GID GId { get { return _GId; } }
		/// <summary> { get; set; }
		/// Globální identifikátor rodičovského záznamu = řádek, v kterém se element nachází.
		/// Pokud je toto čas operace, pak parentem je operace.
		/// Pokud tento element je čas stavu kapacit, jde o kapacitní jednotku.
		/// Pokud tento element představuje část práce z konkrétní operace na konkrétní kapacitě, pak bude zobrazen na dané kapacitě.
		/// Jde o aktuální údaj, který může obsahovat editovanou hodnotu.
		/// </summary>
		public GID RowGId { get { return _RowGId; } set { _RowGId = value; } }
		/// <summary> { get; }
		/// Globální identifikátor rodičovského záznamu = řádek, v kterém se element nachází.
		/// Jde o původní údaj, načtený z databáze. Grafický element jej nemůže změnit.
		/// </summary>
		public GID OriginalRowGId { get { return _OriginalRowGId; } }
		/// <summary> { get; }
		/// Globální identifikátor pracovní jednotky: jedna pracovní jednotka může být zobrazena ve více grafech (různé typy grafů),
		/// a v rámci jednoho grafu může být jedna práce zobrazena ve více časových elementech (časově roztrhaná práce), ale přesto je to jeden pracovní úkol.
		/// </summary>
		public GID WorkGId { get { return _WorkGId; } }
		/// <summary>
		/// Globální identifikátor nadřízené pracovní jednotky: původce tohoto pracovního úseku.
		/// Pokud tento element je práce na operaci, pak parentem je výrobní příkaz/dílec.
		/// Pokud tento element je dílec, pak parentem může být zakázka, nebo nic.
		/// Pokud tento element je stav kapacit, pak parenta nemá: do parenta se promítají změny v čase této operace, a stav kapacit nelze změnit přetáhnutím.
		/// </summary>
		public GID ParentWorkGId { get { return _ParentWorkGId; } set { _ParentWorkGId = value; } }
		/// <summary>
		/// Globální identifikátor podřízených záznamů. Obsahuje WorkGId záznamů elementů, které jsou podřízené tomuto elementu.
		/// Lze tak sestavit mapu (strom) 
		/// </summary>
		public GID[] FirstChildWorkGIds { get { return _FirstChildWorkGIds; } set { _FirstChildWorkGIds = value; } }
		/// <summary> { get; set; }
		/// Globální identifikátor WorkGId pracovních úkolů, které předcházejí výkonu práce na elementech this.WorkGId
		/// </summary>
		public GID[] PreviousWorkGIds { get { return _PreviousWorkGIds; } set { _PreviousWorkGIds = value; } }
		/// <summary> { get; set; }
		/// Globální identifikátor WorkGId pracovních úkolů, které následují po výkonu práce na elementech this.WorkGId
		/// </summary>
		public GID[] NextWorkGIds { get { return _NextWorkGIds; } set { _NextWorkGIds = value; } }
		/// <summary> { get; set; }
		/// Časový interval tohoto elementu (od kdy do kdy trvá).
		/// Jde o aktuální údaj, který může obsahovat editovanou hodnotu.
		/// </summary>
		public TimeRange TimeRange { get { return _TimeRange; } set { _TimeRange = value; } }
		/// <summary> { get; }
		/// Časový interval tohoto elementu (od kdy do kdy trvá).
		/// Jde o původní údaj, načtený z databáze. Grafický element jej nemůže změnit.
		/// </summary>
		public TimeRange OriginalTimeRange { get { return _OriginalTimeRange; } }
		/// <summary>
		/// Zlomky elementu, převezme si je vhodný painter
		/// </summary>
		public float[] Fragments { get { return this._Fragments; } set { this._Fragments = value; } }
		/// <summary> { get; set; }
		/// Množství pracovní energie, typicky počet sekund práce na jednom průchodu.
		/// Pro energii platí zákon o zachování energie: nelze ji ztratit, nelze ji přidat. 
		/// Je možno ji rozdělovat na menší úseky, anebo ji slučovat do jednoho úseku.
		/// Ale součet energie na konci musí být v rámci jednoho WorkGId stejný, jako na jeho počátku.
		/// </summary>
		public decimal WorkEnergy { get { return _WorkEnergy; } set { _WorkEnergy = value; } }
		/// <summary> { get; }
		/// Stav editace tohoto elementu
		/// </summary>
		public GraphEditedState EditedState { get { return _GetEditedState(); } }
		/// <summary>
		/// Určí stav editace na základě dat v objektu.
		/// Klíčová data jsou uložena ve verzi Original i jako Instance, jejich porovnáním lze určit stav editace.
		/// Nově přidané věty mají hodnoty Original prázdné, což data načtená z databáze nikdy nemají.
		/// </summary>
		/// <returns></returns>
		private GraphEditedState _GetEditedState()
		{
			bool currentValid = !this.TimeRange.IsEmpty && !this.RowGId.IsEmpty; // Nynější data jsou platná, pokud obsahují notEmpty čas a notEmpty řádek. Empty čas == MinValue, nejde jen o časový úsek s nulovou délkou (ten není Empty, jen má Seconds == 0).
			bool loadFromDb = this.OriginalRowGId.NotEmpty;                      // Data byla načtena z databáze, pokud původní RowGID není prázdný. Z databáze nelze načíst element s RowGID = Empty. Nepoužívám ElementGID, ten není Empty ani u nově přidaných položek = nové položky používají GID = Temp, ten není Empty.
			bool difference = ((this.TimeRange != this.OriginalTimeRange) || (this.RowGId != this.OriginalRowGId));  // true = je zde rozdíl mezi Original a Instance verzí
			if (currentValid)
			{	// Obsahuje platná data:
				if (!loadFromDb) return GraphEditedState.New;        // Nejsou z databáze => jsou nová, budou se Insertovat
				if (difference) return GraphEditedState.Edited;      // Jsou změněná => budou se Updatovat
				return GraphEditedState.Unchanged;                   // Jsou z databáze, ale nezměněná => nic s nimi neděláme. Možná je zkontrolujeme?
			}
			// Nyní obsahuje neplatná data:
			if (loadFromDb) return GraphEditedState.Deleted;         // Zrušená data z databáze => Delete
			return GraphEditedState.Ignore;                          // Vložená data, zrušená => ignorovat.
		}
		/// <summary> { get; }
		/// Příslušnost data do vrstvy.
		/// Odlišuje vrstvu podkladovou (typicky zobrazení směny) a vrstvu datovou (aktivita v čase, která může být delší / kratší než směna).
		/// </summary>
		public GraphElementLayerType ElementLayer { get { return _ElementLayer; } }
		/// <summary> { get; }
		/// Definuje režim, jakým lze touto položkou pohybovat ( Move a Resize )
		/// a jaký vliv má tento pohyb na okolní položky ( nadřízené a sousední ).
		/// Tato proměnná má význam pouze pro vrstvu (ElementLayer == GraphElementLayerType.ItemLayer).
		/// </summary>
		public DataElementEditingMode ElementEditingMode { get { return _ElementEditingMode; } }
		/// <summary>
		/// Tvar elementu.
		/// </summary>
		public GraphElementShapeType ElementShapeType { get { return this._ElementShapeType; } }
		/// <summary>
		/// Barva pozadí elementu
		/// </summary>
		public Color BackColor { get { return this._BackColor; } }
		/// <summary>
		/// Barva písma v elementu
		/// </summary>
		public Color FontColor { get { return this._FontColor; } }
		/// <summary> { get; }
		/// Specifikuje druh obsahu, ovlivňuje způsob zobrazování (barvu, vzhled).
		/// Datová vrstva zde může odlišovat různé typy směn, různé režimy práce, různé úrovně hierarchie zakázky, různé zobrazované třídy.
		/// U grafu typu RowGraphMode.Gantt ovlivní barvu políčka, vyjadřuje druh činnosti.
		/// U grafu typu RowGraphMode.CapacityUtilization ovlivní barvu políčka, vyjadřuje druh směny.
		/// U grafu typu RowGraphMode.TaskCapacityLink ovlivňuje barvu políčka směny (pokud je this.ElementLayer == SubLayer), nebo práce (pokud je this.ElementLayer == ItemLayer)
		/// Konkrétní barvu generuje metoda IDataSource.GetColorForElement()
		/// </summary>
		public GraphElementVisualType ElementVisualType { get { return _ElementVisualType; } }
		/// <summary> { get; }
		/// Splnění úkolu, typicky u operací, které jsou částečně nebo zcela odvedeny.
		/// Zohledňuje se u grafu typu Gantt. (Typ grafu určuje RowGId (typ řádku).)
		/// Smysluplné hodnoty: 0 (= ještě se ani nezačalo) až 1 (= vše je hotovo). Hodnoty mimo rozsah 0 až 1 mají význam 0 nebo 1.
		/// </summary>
		public float CompletionRatio { get { return _CompletionRatio; } }
		/// <summary> { get; }
		/// Využití stavu kapacity v poměrné hodnotě (1.00 = 100%). Záporné hodnoty mají význam jako 0. 
		/// Hodnoty nad 1.00 značí přečerpání kapacit a zobrazují se.
		/// </summary>
		public float UtilizationRatio { get { return _UtilizationRatio; } }
		/// <summary>
		/// Vrátí text, který se zobrazuje v ToolTipu.
		/// Může vrátit null, pak se za tento objekt nic nezobrazí.
		/// Může vrátit text, obsahující CrLf, pak bude text odřádkován.
		/// </summary>
		public string ToolTipInfo(IDataRow rowData)
		{
			// Podle typu grafu:
			string timeRange = "Čas od: " + this.TimeRange.Begin.ToString() + "  do: " + this.TimeRange.End.ToString();
			string eol = "\r\n";
			string result = null;
			double percent;
			if (this.ElementLayer == GraphElementLayerType.TempLayer)
				result = timeRange;
			else
			{
				switch (rowData.RowGraph)
				{
					case RowGraphMode.CapacityUtilization:
						percent = Math.Round(this.UtilizationRatio * 100F, 1);
						result = timeRange + eol + "Kapacita využita na " + percent.ToString() + "%";
						break;
					case RowGraphMode.Gantt:
						result = timeRange;
						percent = Math.Round(this.CompletionRatio * 100F, 1);
						if (percent > 0F)
							result += eol + "Dokončeno " + percent.ToString() + "%";
						break;
					case RowGraphMode.TaskCapacityLink:
						result = timeRange;
						break;
				}
			}
			// TextNote:
			string eols = "\r\n      ";
			if (this.TextNote != null)
				result += eols + this.TextNote.Replace("\r\n", eols);

			return result;
		}
		/// <summary>
		/// Vrátí text, který se zobrazuje v obdélníku elementu při jeho "zaměření", například při editaci.
		/// Jde o krátkou, zásadně jednořádkovou informaci, jejíž délka (počet znaků) je omezena 
		/// fyzickou velikostí elementu při aktuálním rozlišení.
		/// </summary>
		/// <param name="rowData">Data o řádku, v němž se element nachází. Řádek může obsahovat některé potřebné informace.</param>
		/// <param name="charCount">Nejvyšší počet aktuálně zobrazitelných znaků. 
		/// Pokud bude vrácený text obsahovat více znaků, budou pravděpodobně oříznuty. 
		/// Pravděpodobnost je způsobena proporcionálním písmem. 
		/// Daný počet znaků je vypočten na maximální šířku znaku, jde tedy o garantovaný zobrazitelný počet. 
		/// Pokud bude vrácen text delší, ale složený z užších znaků, možná se podaří je zobrazit korektně. 
		/// Jsme v grafickém prostředí Windows...</param>
		/// <returns></returns>
		public string Caption(IDataRow rowData, float charCount)
		{
			string result = rowData.Name;

			if (this.TextCaptions == null) return result;

			foreach (string text in this.TextCaptions)
			{
				if (text == null) continue;
				string caption = text.Trim();

				if (caption.Length < charCount)
					result = caption;
				else
					break;
			}

			return result;
		}
		#endregion
		#region KONSTRUKTORY A VIZUALIZACE
		public PlanningVisualDataElementCls(GID gId, GID rowGId, GID workGId, TimeRange timeRange)
		{
			this._GId = gId;
			this._RowGId = rowGId;
			this._OriginalRowGId = rowGId;
			this._WorkGId = workGId;
			this._TimeRange = timeRange;
			this._OriginalTimeRange = timeRange;
			this._ElementLayer = GraphElementLayerType.ItemLayer;
			this._ElementEditingMode = DataElementEditingMode.DefaultEditingState;
			this._ElementShapeType = GraphElementShapeType.Rectangle;
			this._ElementVisualType = DefaultVisualType;
			this._UtilizationRatio = 0F;
			this._CompletionRatio = 0F;
		}
		public PlanningVisualDataElementCls(GID gId, GID rowGId, GID workGId, TimeRange timeRange, DataElementEditingMode moveMode, GraphElementShapeType elementShapeType)
		{
			this._GId = gId;
			this._RowGId = rowGId;
			this._OriginalRowGId = rowGId;
			this._WorkGId = workGId;
			this._TimeRange = timeRange;
			this._OriginalTimeRange = timeRange;
			this._ElementLayer = GraphElementLayerType.ItemLayer;
			this._ElementEditingMode = moveMode;
			this._ElementShapeType = elementShapeType;
			this._ElementVisualType = DefaultVisualType;
			this._UtilizationRatio = 0F;
			this._CompletionRatio = 0F;

		}
		public PlanningVisualDataElementCls(GID gId, GID rowGId, GID workGId, TimeRange timeRange, DataElementEditingMode moveMode)
		{
			this._GId = gId;
			this._RowGId = rowGId;
			this._OriginalRowGId = rowGId;
			this._WorkGId = workGId;
			this._TimeRange = timeRange;
			this._OriginalTimeRange = timeRange;
			this._ElementLayer = GraphElementLayerType.ItemLayer;
			this._ElementEditingMode = moveMode;
			this._ElementVisualType = DefaultVisualType;
			this._UtilizationRatio = 0F;
			this._CompletionRatio = 0F;

		}
		public PlanningVisualDataElementCls(GID gId, GID rowGId, GID workGId, TimeRange timeRange, float completionRatio)
		{
			this._GId = gId;
			this._RowGId = rowGId;
			this._OriginalRowGId = rowGId;
			this._WorkGId = workGId;
			this._TimeRange = timeRange;
			this._OriginalTimeRange = timeRange;
			this._ElementLayer = GraphElementLayerType.ItemLayer;
			this._ElementEditingMode = DataElementEditingMode.DefaultEditingState;
			this._ElementVisualType = DefaultVisualType;
			this._UtilizationRatio = 0F;
			this._CompletionRatio = completionRatio;
		}
		/// <summary>
		/// Vytvoří a vrátí Temporary element na základě požadavku na načítání dat.
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		public static PlanningVisualDataElementCls CreateTempElement(DataSourceRequest request)
		{
			PlanningVisualDataElementCls element = new PlanningVisualDataElementCls(request.TempGId, request.RequestRowGId, request.RequestedWorkGId, request.RequestedTimeRange);
			element.SetElementProperty(GraphElementLayerType.TempLayer, DataElementEditingMode.FixedItem, GraphElementVisualType.Type1);
			return element;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{Element GID=" + this.GId.ToString() + "; Parent=" + this.RowGId.ToString() + "; Time=" + this.TimeRange.ToString() + "}";
		}
		#endregion
		#region FIELDS
		private GID _GId;
		private GID _RowGId;
		private GID _OriginalRowGId;
		private GID _WorkGId;
		private GID _ParentWorkGId;
		private GID[] _FirstChildWorkGIds;
		private GID[] _PreviousWorkGIds;
		private GID[] _NextWorkGIds;
		private TimeRange _TimeRange;
		private TimeRange _OriginalTimeRange;
		private float[] _Fragments;
		private decimal _WorkEnergy;
		private GraphElementLayerType _ElementLayer;
		private DataElementEditingMode _ElementEditingMode;
		private GraphElementShapeType _ElementShapeType;
		private Color _BackColor;
		private Color _FontColor;
		private GraphElementVisualType _ElementVisualType;
		private float _CompletionRatio;
		private float _UtilizationRatio;
		/// <summary>
		/// Může obsahovat rozšiřující text, který bude vracet metoda ToolTip.
		/// </summary>
		public string TextNote;
		/// <summary>
		/// Může obsahovat pole stringů, od nejkratšího po nejkomplexnější, které slouží jako zdroj popisku pro element v době jeho editace.
		/// Pokud je null, pak jako popisek bude sloužit text řádku.
		/// Pokud není null, mělo by pole obsahovat popisky v různé délce, počínaje nejkratším až po nejdelší.
		/// Metoda this.Caption() si pak vybere jeden text podle vhodné délky, pro aktuální velikost grafu.
		/// </summary>
		public string[] TextCaptions;
		#endregion
		#region POMOCNÉ METODY
		/// <summary>
		/// Defaultní vizuální styl objektu, který se běžně použije v konstruktoru. Odvozuje vizuální styl podle třídy.
		/// Ve vytvářeném objektu je vložen do this.ElementVisualType. Tam je možno styl upravit.
		/// </summary>
		protected GraphElementVisualType DefaultVisualType
		{
			get
			{	// Vizuální typ = barevnost.
				// Může se odvíjet od (this._GId.NClass) = třída elementu (tj. např. čerpání stavu kapacit)
				// anebo od (this._RowGId.NClass) = třída řádku (tj. např. výrobní příkaz, plán, atd)
				switch (this._RowGId.ClassNumber)
				{
					case 0x04A4:
						return GraphElementVisualType.Type1;
					case 0x0501:
						return GraphElementVisualType.Type2;
					case 0x0502:
						return GraphElementVisualType.Type3;
					case 0x0480:
						return GraphElementVisualType.Type6;
				}
				return GraphElementVisualType.Type1;
			}
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		public void SetElementProperty(GraphElementVisualType visualType)
		{
			this._ElementVisualType = visualType;
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		public void SetElementProperty(GraphElementShapeType elementShapeType)
		{
			this._ElementShapeType = elementShapeType;
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="editingMode">Typ editace</param>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		public void SetElementProperty(DataElementEditingMode editingMode, GraphElementVisualType visualType)
		{
			this._ElementEditingMode = editingMode;
			this._ElementVisualType = visualType;
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="layerType">Typ vrstvy elementu</param>
		/// <param name="editingMode">Typ editace</param>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		public void SetElementProperty(GraphElementLayerType layerType, DataElementEditingMode editingMode, GraphElementVisualType visualType)
		{
			this._ElementLayer = layerType;
			this._ElementEditingMode = editingMode;
			this._ElementVisualType = visualType;
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="layerType">Typ vrstvy elementu</param>
		/// <param name="editingMode">Typ editace</param>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		public void SetElementProperty(GraphElementLayerType layerType, DataElementEditingMode editingMode, GraphElementVisualType visualType, GraphElementShapeType elementShapeType)
		{
			this._ElementLayer = layerType;
			this._ElementEditingMode = editingMode;
			this._ElementShapeType = elementShapeType;
			this._ElementVisualType = visualType;
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="layerType">Typ vrstvy elementu</param>
		/// <param name="editingMode">Typ editace</param>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		/// <param name="completionRatio">Poměr splnění gantu</param>
		/// <param name="utilizationRatio">Poměr využití kapacity</param>
		public void SetElementProperty(GraphElementLayerType layerType, DataElementEditingMode editingMode, GraphElementVisualType visualType, float completionRatio, float utilizationRatio)
		{
			SetElementProperty(layerType, editingMode, visualType);
			this._UtilizationRatio = utilizationRatio;
			this._CompletionRatio = completionRatio;
		}
		/// <summary>
		/// Nastaví barvu pozadí (a popředí)
		/// </summary>
		/// <param name="backColor">Barva pozadí, výplň buňky</param>
		internal void SetColor(Color backColor)
		{
			this._BackColor = backColor;
		}
		/// <summary>
		/// Nastaví barvu pozadí (a popředí)
		/// </summary>
		/// <param name="backColor">Barva pozadí, výplň buňky</param>
		/// <param name="fontColor">Barva písma v buňce</param>
		internal void SetColor(Color backColor, Color fontColor)
		{
			this._BackColor = backColor;
			this._FontColor = fontColor;
		}
		/// <summary>
		/// Nastaví zakotvení tohoto elementu do mapy okolních elementů.
		/// </summary>
		/// <param name="parentWorkGId">WorkGID parenta. Ten je jen jeden.</param>
		/// <param name="previousWorkGIds">GID[] předchozích WorkGID (ne elementy, ale tasky).</param>
		/// <param name="nextWorkGIds">GID[] návazných WorkGID (ne elementy, ale tasky).</param>
		/// <param name="firstChildWorkGIds">GID[] podřízených WorkGID (ne elementy, ale tasky).</param>
		public void SetElementNodes(GID parentWorkGId, GID[] previousWorkGIds, GID[] nextWorkGIds, GID[] firstChildWorkGIds)
		{
			this._ParentWorkGId = parentWorkGId;
			this._PreviousWorkGIds = previousWorkGIds;
			this._NextWorkGIds = nextWorkGIds;
			this._FirstChildWorkGIds = firstChildWorkGIds;
		}
		/// <summary>
		/// Uloží do své paměti texty, které se budou používat jako Caption při editaci elementu jako popisek v řádku.
		/// </summary>
		/// <param name="captions"></param>
		public void SetElementCaptions(params string[] captions)
		{
			this.TextCaptions = captions;
		}
		#endregion
	}
	#endregion
	#region CLASS VisualTaskTimeCls : střadač / konvertor / exportér jednotlivých fází jedné výrobní operace
	/// <summary>
	/// VisualTaskTimeCls : střadač / konvertor / exportér jednotlivých fází jedné výrobní operace 
	/// </summary>
	internal class VisualTaskTimeCls
	{
		#region KONSTRUKCE
		private VisualTaskTimeCls(DateTime stockEnd)
		{
			this._SecondsTMC = 0D;
			this._SecondsTBC = 0D;
			this._SecondsTAC = 0D;
			this._SecondsTEC = 0D;
			this._TimeWork = TimeRange.Empty;
			this._StockEnd = stockEnd;
			this._Calculated = false;
		}
		/// <summary>Součet sekund fáze MoveTime</summary>
		private double _SecondsTMC;
		/// <summary>Součet sekund fáze TBC</summary>
		private double _SecondsTBC;
		/// <summary>Součet sekund fáze TAC</summary>
		private double _SecondsTAC;
		/// <summary>Součet sekund fáze TEC</summary>
		private double _SecondsTEC;
		/// <summary>Sloučený čas všech fází všech operací / průchodů, mimo čas TSC, který se nachází za _TimeWork.End do _StockEnd</summary>
		private TimeRange _TimeWork;
		/// <summary>Koncové datum pohybu = výsledná výdejka (do poptávky, do komponenty)</summary>
		private DateTime _StockEnd;
		/// <summary>Příznak, že výsledky _Result jsou platné</summary>
		private bool _Calculated;
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{Time=" + this.TimeReal + "; Density=" + this.TotalDensity + "}";
		}
		#endregion
		#region REKALKULACE
		/// <summary>
		/// Zajistí, že budou vypočteny výsledky do proměnných _Result
		/// </summary>
		private void _CheckCalc()
		{
			if (this._Calculated) return;

			double secondsWork = this._SecondsTMC + this._SecondsTBC + this._SecondsTAC + this._SecondsTEC;
			TimeRange timeWork = this._TimeWork;
			if ((timeWork.IsEmpty || timeWork.Seconds <= 0D) && this._StockEnd > DateTime.MinValue)
			{	// Čas práce dosud není zadán, ale známe čas konce: vytoříme jej podle počtu sekund (nejspíš bude 0):
				timeWork = new TimeRange(this._StockEnd, this._StockEnd);
				if (secondsWork > 0D)
					// Pokud je počet sekund kladný, tak vytvořím časový interval od daného konce, určím odpovídající Begin:
					timeWork.SetSeconds(secondsWork, TimeRange.TimeDirection.ToHistory);
			}
			// Mám korektní timeWork, vytvoříme časy konců jednotlivých fází:
			double secondsKfc = (secondsWork > 0D ? timeWork.Seconds / secondsWork : 0D);     // Koeficient
			DateTime timeMB = timeWork.Begin;
			DateTime timeME = timeMB.AddSeconds((this._SecondsTMC) * secondsKfc);
			DateTime timeBE = timeMB.AddSeconds((this._SecondsTMC + this._SecondsTBC) * secondsKfc);
			DateTime timeAE = timeMB.AddSeconds((this._SecondsTMC + this._SecondsTBC + this._SecondsTAC) * secondsKfc);
			DateTime timeEE = timeWork.End;
			DateTime timeSE = (this._StockEnd > timeEE ? this._StockEnd : timeEE);

			this._TimeTMC = new TimeRange(timeMB, timeME);
			this._TimeTBC = new TimeRange(timeME, timeBE);
			this._TimeTAC = new TimeRange(timeBE, timeAE);
			this._TimeTEC = new TimeRange(timeAE, timeEE);
			this._TimeTSC = new TimeRange(timeEE, timeSE);
			this._TimeReal = new TimeRange(timeMB, timeSE);

			// Přznak OK:
			this._Calculated = true;
		}
		#endregion
		#region PROPERTY
		/// <summary>Součet počtu sekund času TMC</summary>
		internal float SecondsTMC { get { return (float)(this._SecondsTMC); } }
		/// <summary>Součet počtu sekund času TBC</summary>
		internal float SecondsTBC { get { return (float)(this._SecondsTBC); } }
		/// <summary>Součet počtu sekund času TAC</summary>
		internal float SecondsTAC { get { return (float)(this._SecondsTAC); } }
		/// <summary>Součet počtu sekund času TEC</summary>
		internal float SecondsTEC { get { return (float)(this._SecondsTEC); } }
		/// <summary>Čas konce operace/tasku/záznamu</summary>
		internal DateTime StockEnd { get { return this._StockEnd; } }
		/// <summary>Čas celkový, od počátku TMC do konce StockEnd</summary>
		internal TimeRange TimeReal { get { this._CheckCalc(); return this._TimeReal; } }
		private TimeRange _TimeReal;
		/// <summary>Čas fáze MoveTime</summary>
		internal TimeRange TimeTMC { get { this._CheckCalc(); return this._TimeTMC; } }
		private TimeRange _TimeTMC;
		/// <summary>Čas fáze TBC</summary>
		internal TimeRange TimeTBC { get { this._CheckCalc(); return this._TimeTBC; } }
		private TimeRange _TimeTBC;
		/// <summary>Čas fáze TAC</summary>
		internal TimeRange TimeTAC { get { this._CheckCalc(); return this._TimeTAC; } }
		private TimeRange _TimeTAC;
		/// <summary>Čas fáze TEC</summary>
		internal TimeRange TimeTEC { get { this._CheckCalc(); return this._TimeTEC; } }
		private TimeRange _TimeTEC;
		/// <summary>Čas fáze TSC</summary>
		internal TimeRange TimeTSC { get { this._CheckCalc(); return this._TimeTSC; } }
		private TimeRange _TimeTSC;
		/// <summary>
		/// Obsahuje součet sekund jednotlivých časů
		/// </summary>
		internal double TotalSeconds { get { return (this._SecondsTMC + this._SecondsTBC + this._SecondsTAC + this._SecondsTEC + this.TimeTSC.Seconds); } }
		/// <summary>
		/// Hustota práce v času
		/// </summary>
		internal double TotalDensity { get { return (this.TotalSeconds / this.TimeReal.Seconds); } }
		#endregion
		#region STATIC KONSTRUKTORY
		/// <summary>
		/// Vrátí vždy nový prázdný objekt
		/// </summary>
		internal static VisualTaskTimeCls Empty { get { return new VisualTaskTimeCls(DateTime.MinValue); } }
		/// <summary>
		/// Vytvoří a vrátí nový objekt a vyplní do něj okamžik konce
		/// </summary>
		/// <param name="task"></param>
		/// <param name="stockTimeRange"></param>
		/// <returns></returns>
		internal static VisualTaskTimeCls CreateFromEnd(DateTime stockEnd)
		{
			VisualTaskTimeCls time = new VisualTaskTimeCls(stockEnd);
			return time;
		}
		/// <summary>
		/// Vytvoří a vrátí nový objekt a naplní do něj data z předaného tasku
		/// </summary>
		/// <param name="task"></param>
		/// <param name="stockTimeRange"></param>
		/// <returns></returns>
		internal static VisualTaskTimeCls CreateFromTask(DateTime stockEnd, CapacityPlanWorkItemCls task)
		{
			VisualTaskTimeCls time = new VisualTaskTimeCls(stockEnd);
			time.AddTimeFromTask(task);
			return time;
		}
		#endregion
		#region PŘIDÁVÁNÍ ČASU
		/// <summary>
		/// Přidá do sebe časy z předané operace (projde všechny Pass)
		/// </summary>
		/// <param name="task"></param>
		internal void AddTimeFromTask(CapacityPlanWorkItemCls task)
		{
			foreach (WorkPassCls pass in task.WorkPassList)
				this.AddTimeFromPass(pass);
		}
		/// <summary>
		/// Přidá do sebe časy z předaného pracovního průchodu (jednotka operace)
		/// </summary>
		/// <param name="pass"></param>
		internal void AddTimeFromPass(WorkPassCls pass)
		{
			this._SecondsTMC += this._GetSeconds(pass.TimeTMC);
			this._SecondsTBC += this._GetSeconds(pass.TimeTBC);
			this._SecondsTAC += this._GetSeconds(pass.TimeTAC);
			this._SecondsTEC += this._GetSeconds(pass.TimeTEC);
		}
		/// <summary>
		/// Přidá do sebe časy z předaného jiného času
		/// </summary>
		/// <param name="pass"></param>
		internal void AddTimeFromTime(VisualTaskTimeCls time)
		{
			// Pokud ten druhý čas končí později, vezmu ten druhý čas:
			if (this._StockEnd < time._StockEnd) this._StockEnd = time._StockEnd;

			// Sečtu sekundy:
			this._SecondsTMC += time._SecondsTMC;
			this._SecondsTBC += time._SecondsTBC;
			this._SecondsTAC += time._SecondsTAC;
			this._SecondsTEC += time._SecondsTEC;

			// Sečtu časové okno Work:
			this._TimeWork = this._TimeWork + time._TimeWork;

			this._Calculated = false;
		}
		/// <summary>
		/// Zaregistruje časový interval operace do this._TimeWork, a vrátí počet sekund času
		/// </summary>
		/// <param name="timeRange"></param>
		/// <returns></returns>
		private double _GetSeconds(TimeRange timeRange)
		{
			this._TimeWork = this._TimeWork + timeRange;
			this._Calculated = false;
			return timeRange.Seconds;
		}
		#endregion
	}
	#endregion
}
